﻿using System.Linq;

namespace Bogus.Extensions.Canada;

/// <summary>
/// API extensions specific for a geographical location.
/// </summary>
public static class ExtensionsForCanada
{
   private static int[] Mask = {1, 2, 1, 2, 1, 2, 1, 2, 1};

   /// <summary>
   /// Social Insurance Number for Canada 
   /// </summary>
   public static string Sin(this Person p)
   {
      const string Key = nameof(ExtensionsForCanada) + "SIN";
      if( p.context.TryGetValue(Key, out var value) )
      {
         return (string)value;
      }

      //bit verbose, but works. :)
      //could be mathematically simplified.
      //brute forced this one. yeah.
      //d
      //should pass basic validation, but only some 
      //numbers dont start with 8 etc.

      /*
      1 — Atlantic Provinces: Nova Scotia, New Brunswick, Prince Edward Island, and Newfoundland and Labrador (this may also cover overseas residents).
      2–3 — Quebec
      4–5 — Ontario (#4 includes overseas forces)
      6 — Prairie Provinces (Manitoba, Saskatchewan, and Alberta), Northwest Territories, and Nunavut
      7 — Pacific Region (British Columbia and Yukon)
      8 — Not used
      9 — Temporary resident
      0 — Not used (Canada Revenue may assign fictitious SIN numbers beginning with zero to taxpayers who do not have SINs)
      */

      var r = p.Random;
      //get 8 numbers
      var numbers = r.Digits(8);

      // the last number that makes it pass the checksum.
      var last = 10 - (numbers.Sum() % 10);
      if( last == 10 )
         last = 0;

      var digits = numbers.Concat(new[] {last});

      var comp = digits
         .Zip(Mask, (n, c) =>
            {
               if( c == 2 && n % c == 1 )
               {
                  // odd digit, it was multiplied, reverse the process
                  return (10 + (n - 1)) / 2;
               }
               if( c == 2 )
               {
                  //simply divide an even number by two
                  return n / 2;
               }
               //else c == 1, and n was multiplied by 1
               return n;
            }).ToArray();


      var sinstr = $"{comp[0]}{comp[1]}{comp[2]} {comp[3]}{comp[4]}{comp[5]} {comp[6]}{comp[7]}{comp[8]}";

      p.context[Key] = sinstr;

      return sinstr;
   }
}